// 版权所有2009年围棋作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// 包hex实现十六进制编码和解码。
package hex

import (
	"errors"
	"fmt"
	"io"
	"strings"
)

const hextable = "0123456789abcdef"

// EncodedLen返回n个源字节的编码长度。
// 具体来说，它返回n*2。
func EncodedLen(n int) int { return n * 2 }

// Encode将src编码为EncodedLen（len（src））
// dst的字节数。为了方便起见，它返回数字
// 写入dst的字节数，但此值始终为EncodedLen（len（src））。
// Encode实现十六进制编码。
func Encode(dst, src []byte) int {
	j := 0
	for _, v := range src {
		dst[j] = hextable[v>>4]
		dst[j+1] = hextable[v&0x0f]
		j += 2
	}
	return len(src) * 2
}

// ErrLength报告尝试解码奇数长度输入
// 使用解码或解码字符串。
// 基于流的解码器返回io.ErrUnexpectedEOF而不是ErrLength。
var ErrLength = errors.New("encoding/hex: odd length hex string")

// InvalidByteError值描述由十六进制字符串中的无效字节导致的错误。
type InvalidByteError byte

func (e InvalidByteError) Error() string {
	return fmt.Sprintf("encoding/hex: invalid byte: %#U", rune(e))
}

// DecodedLen返回x个源字节的解码长度。
// 具体来说，它返回x/2。
func DecodedLen(x int) int { return x / 2 }

// 解码将src解码为DecodedLen（len（src））字节，
// 返回写入dst的实际字节数。
// None
// Decode要求src只包含十六进制
// 字符，并且src具有偶数长度。
// 如果输入格式不正确，Decode将返回数字
// 在发生错误之前解码的字节数。
func Decode(dst, src []byte) (int, error) {
	i, j := 0, 1
	for ; j < len(src); j += 2 {
		a, ok := fromHexChar(src[j-1])
		if !ok {
			return i, InvalidByteError(src[j-1])
		}
		b, ok := fromHexChar(src[j])
		if !ok {
			return i, InvalidByteError(src[j])
		}
		dst[i] = (a << 4) | b
		i++
	}
	if len(src)%2 == 1 {
		// 在报告错误长度之前检查无效字符，
		// 因为无效字符（如果存在）是早期的问题。
		if _, ok := fromHexChar(src[j-1]); !ok {
			return i, InvalidByteError(src[j-1])
		}
		return i, ErrLength
	}
	return i, nil
}

// fromHexChar将十六进制字符转换为其值和成功标志。
func fromHexChar(c byte) (byte, bool) {
	switch {
	case '0' <= c && c <= '9':
		return c - '0', true
	case 'a' <= c && c <= 'f':
		return c - 'a' + 10, true
	case 'A' <= c && c <= 'F':
		return c - 'A' + 10, true
	}

	return 0, false
}

// EncodeToString返回src的十六进制编码。
func EncodeToString(src []byte) string {
	dst := make([]byte, EncodedLen(len(src)))
	Encode(dst, src)
	return string(dst)
}

// DecodeString返回由十六进制字符串s表示的字节。
// None
// DecodeString要求src只包含十六进制
// 字符，并且src具有偶数长度。
// 如果输入格式不正确，DecodeString将返回
// 错误发生前解码的字节。
func DecodeString(s string) ([]byte, error) {
	src := []byte(s)
	// 我们可以使用源片本身作为目标
	// 因为解码循环增加1，然后不再使用“seen”字节。
	n, err := Decode(src, src)
	return src[:n], err
}

// Dump返回包含给定数据十六进制转储的字符串。格式
// 十六进制转储的输出与命令行上的`hextump-C`的输出相匹配。
func Dump(data []byte) string {
	if len(data) == 0 {
		return ""
	}

	var buf strings.Builder
	// 转储程序将为每个完整的16字节块写入79字节，并且至少
	// 64字节表示剩余的内容。四舍五入分配，因为只有一个
	// 最多将浪费15个字节。
	buf.Grow((1 + ((len(data) - 1) / 16)) * 79)

	dumper := Dumper(&buf)
	dumper.Write(data)
	dumper.Close()
	return buf.String()
}

// bufferSize是编码器和解码器中要缓冲的十六进制字符数。
const bufferSize = 1024

type encoder struct {
	w   io.Writer
	err error
	out [bufferSize]byte // 输出缓冲区
}

// NewEncoder返回一个io.Writer，它将小写十六进制字符写入w。
func NewEncoder(w io.Writer) io.Writer {
	return &encoder{w: w}
}

func (e *encoder) Write(p []byte) (n int, err error) {
	for len(p) > 0 && e.err == nil {
		chunkSize := bufferSize / 2
		if len(p) < chunkSize {
			chunkSize = len(p)
		}

		var written int
		encoded := Encode(e.out[:], p[:chunkSize])
		written, e.err = e.w.Write(e.out[:encoded])
		n += written / 2
		p = p[chunkSize:]
	}
	return n, e.err
}

type decoder struct {
	r   io.Reader
	err error
	in  []byte           // 输入缓冲区（编码形式）
	arr [bufferSize]byte // in的支持阵列
}

// NewDecoder返回一个io.Reader，用于解码r中的十六进制字符。
// NewDecoder期望r只包含偶数个十六进制字符。
func NewDecoder(r io.Reader) io.Reader {
	return &decoder{r: r}
}

func (d *decoder) Read(p []byte) (n int, err error) {
	// 用足够的字节填充内部缓冲区进行解码
	if len(d.in) < 2 && d.err == nil {
		var numCopy, numRead int
		numCopy = copy(d.arr[:], d.in) // 复制0或1个字节
		numRead, d.err = d.r.Read(d.arr[numCopy:])
		d.in = d.arr[:numCopy+numRead]
		if d.err == io.EOF && len(d.in)%2 != 0 {
			if _, ok := fromHexChar(d.in[len(d.in)-1]); !ok {
				d.err = InvalidByteError(d.in[len(d.in)-1])
			} else {
				d.err = io.ErrUnexpectedEOF
			}
		}
	}

	// 将内部缓冲区解码为输出缓冲区
	if numAvail := len(d.in) / 2; len(p) > numAvail {
		p = p[:numAvail]
	}
	numDec, err := Decode(p, d.in[:len(p)*2])
	d.in = d.in[2*numDec:]
	if err != nil {
		d.in, d.err = nil, err // 解码错误；丢弃输入余数
	}

	if len(d.in) < 2 {
		return numDec, d.err // 仅在缓冲区完全使用时公开错误
	}
	return numDec, nil
}

// 转储程序返回一个WriteCloser，它将所有写入数据的十六进制转储写入
// W转储的格式与命令上的'hextump-C'的输出匹配
// 线
func Dumper(w io.Writer) io.WriteCloser {
	return &dumper{w: w}
}

type dumper struct {
	w          io.Writer
	rightChars [18]byte
	buf        [14]byte
	used       int  // 当前行中的字节数
	n          uint // 总字节数
	closed     bool
}

func toChar(b byte) byte {
	if b < 32 || b > 126 {
		return '.'
	}
	return b
}

func (h *dumper) Write(data []byte) (n int, err error) {
	if h.closed {
		return 0, errors.New("encoding/hex: dumper closed")
	}

	// 输出行如下所示：
	// 00000010 2e 2f 30 31 32 33 34 35 36 38 39 3a 3b 3c 3d|/0123456789:<=|
	// ^行的偏移量^额外空间^ ASCII。
	for i := range data {
		if h.used == 0 {
			// 在一行的开头，我们打印当前
			// 十六进制偏移量。
			h.buf[0] = byte(h.n >> 24)
			h.buf[1] = byte(h.n >> 16)
			h.buf[2] = byte(h.n >> 8)
			h.buf[3] = byte(h.n)
			Encode(h.buf[4:], h.buf[:4])
			h.buf[12] = ' '
			h.buf[13] = ' '
			_, err = h.w.Write(h.buf[4:])
			if err != nil {
				return
			}
		}
		Encode(h.buf[:], data[i:i+1])
		h.buf[2] = ' '
		l := 3
		if h.used == 7 {
			// 第8个字节后还有一个额外的空格。
			h.buf[3] = ' '
			l = 4
		} else if h.used == 15 {
			// 在这条线的尽头有一个额外的空间
			// 右栏的栏。
			h.buf[3] = ' '
			h.buf[4] = '|'
			l = 5
		}
		_, err = h.w.Write(h.buf[:l])
		if err != nil {
			return
		}
		n++
		h.rightChars[h.used] = toChar(data[i])
		h.used++
		h.n++
		if h.used == 16 {
			h.rightChars[16] = '|'
			h.rightChars[17] = '\n'
			_, err = h.w.Write(h.rightChars[:])
			if err != nil {
				return
			}
			h.used = 0
		}
	}
	return
}

func (h *dumper) Close() (err error) {
	// 有关此格式的详细信息，请参阅Write（）中的注释。
	if h.closed {
		return
	}
	h.closed = true
	if h.used == 0 {
		return
	}
	h.buf[0] = ' '
	h.buf[1] = ' '
	h.buf[2] = ' '
	h.buf[3] = ' '
	h.buf[4] = '|'
	nBytes := h.used
	for h.used < 16 {
		l := 3
		if h.used == 7 {
			l = 4
		} else if h.used == 15 {
			l = 5
		}
		_, err = h.w.Write(h.buf[:l])
		if err != nil {
			return
		}
		h.used++
	}
	h.rightChars[nBytes] = '|'
	h.rightChars[nBytes+1] = '\n'
	_, err = h.w.Write(h.rightChars[:nBytes+2])
	return
}
